package com.paat.autoconvert;


import com.intellij.codeInsight.generation.*;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.ide.util.MemberChooser;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorModificationUtil;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * author: Lipeng.Yu
 * time: 2019/9/18 17:15
 * description:
 */
public class ConvertHandler implements Runnable {
    protected final Project project;
    protected final Editor editor;
    private final MemberChooser<ClassMember> chooser;
    protected final PsiClass psiClass;

    public ConvertHandler(DataContext dataContext, Project project) {
        this.project = project;
        editor = PlatformDataKeys.EDITOR.getData(dataContext);

        PsiFile psiFile = DataKeys.PSI_FILE.getData(dataContext);
        psiClass = OverrideImplementUtil.getContextClass(project, editor, psiFile, false);

        List<EncapsulatableClassMember> classMembers = getEncapsulatableClassMembers(psiClass);

        chooser = new MemberChooser<>(
                classMembers.toArray(new ClassMember[0]), true, true, project);
        chooser.setTitle(getChooserTitle());
        chooser.setCopyJavadocVisible(false);
    }

    public boolean processChooser() {
        chooser.show();

        List<ClassMember> selectedElements = chooser.getSelectedElements();
        if (selectedElements == null) {
            HintManager.getInstance().showErrorHint(editor, getNothingFoundMessage());
            return false;
        }
        if (selectedElements.isEmpty()) {
            HintManager.getInstance().showErrorHint(editor, getNothingAcceptedMessage());
            return false;
        }
        return true;
    }

    protected String getChooserTitle() {
        return "请选择要生成的字段";
    }

    protected String getNothingFoundMessage() {
        return "没有找到要生成的字段";
    }

    protected String getNothingAcceptedMessage() {
        return "没有找到相应字段";
    }

    protected List<EncapsulatableClassMember> getEncapsulatableClassMembers(PsiClass psiClass) {
        final List<EncapsulatableClassMember> result = new ArrayList<>();
        if (psiClass != null && psiClass.getFields() != null && psiClass.getFields().length > 0) {
            for (PsiField field : psiClass.getFields()) {
                if (null != PropertyUtil.findPropertyField(psiClass, field.getName(), false)) {
                    result.add(new PsiFieldMember(field));
                }
            }
        }
        return result;
    }

    @Override
    public void run() {
        if (!EditorModificationUtil.checkModificationAllowed(editor)) {
            return;
        }
        if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), project)) {
            return;
        }
        process(chooser.getSelectedElements());
    }

    protected void process(List<ClassMember> classMembers) {
        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
        Document document = editor.getDocument();

        // 接口名称
        String interfaceName;
        // 泛型名称
        String genericityName;
        PsiReferenceList psiReferenceList = psiClass.getImplementsList();
        PsiJavaCodeReferenceElement[] psiJavaElements = psiReferenceList.getReferenceElements();
        if (psiJavaElements.length > 0) {
            PsiElement psiElement = psiJavaElements[0].getReferenceNameElement();
            interfaceName = psiElement.getText();
            PsiType[] psiType = psiJavaElements[0].getTypeParameters();
            if (psiType.length > 0) {
                genericityName = psiType[0].getPresentableText();
            } else {
                HintManager.getInstance().showErrorHint(editor, "未找到接口中的泛型");
                return;
            }
        } else {
            HintManager.getInstance().showErrorHint(editor, "未找到要实现的接口");
            return;
        }

        String insertStr = splicingConvert(classMembers, interfaceName, genericityName);
        if (insertStr == null || insertStr.length() == 0) {
            HintManager.getInstance().showErrorHint(editor, "未匹配到要实现的接口");
            return;
        }
        // 2701  2703

        String fullText = document.getText();
        int lastIndex = fullText.lastIndexOf("}");
        document.insertString(lastIndex - 1, insertStr);
        commitAndSaveDocument(psiDocumentManager, document);
    }

    private static String SPACE = "    ";
    private static String DSPACE = "        ";
    private static String ENTER = "\n";
    private static String REQPEST_CLASS = "IParamConvert";
    private static String RESPONSE_CLASS = "IRespConvert";

    /**
     * 拼接代码
     *
     * @param classMembers
     * @return
     */
    public String splicingConvert(List<ClassMember> classMembers, String interfaceName, String genericityName) {
        StringBuilder insertText = new StringBuilder(ENTER + SPACE + "@Override" + ENTER);
        if (REQPEST_CLASS.equals(interfaceName)) {
            insertText.append(SPACE).append("public ").append(genericityName).append(" convert() {").append(ENTER)
                    .append(DSPACE).append(genericityName).append(" vo = new ").append(genericityName).append("();").append(ENTER);

            for (ClassMember classMember : classMembers) {
                final PsiElementClassMember elementClassMember = (PsiElementClassMember) classMember;
                PsiField psiField = (PsiField) elementClassMember.getPsiElement();
                insertText.append(DSPACE).append("vo.set").append(captureName(psiField.getName())).append("(this.get")
                        .append(captureName(psiField.getName())).append("());").append(ENTER);
            }
            insertText.append(DSPACE).append("return vo;").append(ENTER)
                    .append(SPACE).append("}").append(ENTER);
        } else if (RESPONSE_CLASS.equals(interfaceName)) {
            insertText.append(SPACE).append("public void convert(").append(genericityName).append(" vo) {").append(ENTER)
                    .append(DSPACE).append("if (null != vo) {").append(ENTER);

            for (ClassMember classMember : classMembers) {
                final PsiElementClassMember elementClassMember = (PsiElementClassMember) classMember;
                PsiField psiField = (PsiField) elementClassMember.getPsiElement();
                insertText.append(DSPACE).append(SPACE).append("this.set").append(captureName(psiField.getName())).append("(")
                        .append("vo.get").append(captureName(psiField.getName())).append("());").append(ENTER);
            }
            insertText.append(DSPACE).append("}").append(ENTER)
                    .append(SPACE).append("}").append(ENTER);
        } else {
            return null;
        }
        return insertText.toString();
    }

    /**
     * 写入文件
     *
     * @param psiDocumentManager
     * @param document
     */
    public static void commitAndSaveDocument(PsiDocumentManager psiDocumentManager, Document document) {
        if (document != null) {
            psiDocumentManager.doPostponedOperationsAndUnblockDocument(document);
            psiDocumentManager.commitDocument(document);
            FileDocumentManager.getInstance().saveDocument(document);
        }
    }

    /**
     * 首字母大写
     *
     * @param name
     * @return
     */
    public static String captureName(String name) {
        char[] cs = name.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);
    }

}